package C2DMRelated;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLEncoder;
import java.util.logging.Level;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;

import Database.Database;
import Logger.AppLogger;

public class C2DMSender
{


	//***************************************Class Variables*******************************************//


	private Database _db;


	//***************************************Constructors*******************************************//


	public C2DMSender()
	{
		_db = Database.getInstance();
	}


	//***************************************Public Methods*******************************************//



	public void sendC2DMMessage(String collapseKey, String to_registrationId, C2DMMessage newC2dmMessage) 
	{
		sendC2DMMessage(collapseKey, to_registrationId, newC2dmMessage.toString());
	}


	public void sendC2DMMessage(String collapseKey, String registrationId, String message)
	{
		try 
		{
			sendMessage(_db.getAuthToken(), collapseKey, registrationId, message);
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
		}

		AppLogger.write(Level.INFO, "messages were sent");
	}


	//---------------------------------------code for sending messages-------------------------


	private static class FakeHostnameVerifier 
	implements HostnameVerifier 
	{ 
		public boolean verify(String hostname, SSLSession session) 
		{ 
			return true; 
		} 
	} 


	//askes for a new AuthToken and return it!
	private String retrieveNewAuthToken()
	{
		String authToken = null;

		AppLogger.write(Level.INFO, "asking C2DM server for auth token...");

		StringBuilder buf = new StringBuilder();
		HttpsURLConnection.setDefaultHostnameVerifier(new FakeHostnameVerifier()); 
		HttpsURLConnection request = null;
		OutputStreamWriter post = null;
		try 
		{
			URL url = new URL("https://www.google.com/accounts/ClientLogin");
			request = (HttpsURLConnection) url.openConnection();
			request.setDoOutput(true);
			request.setDoInput(true);

			String SENDER_ID = new String("reuvenlevitsky@gmail.com");
			String SENDER_PW = new String("089722183");

			buf.append("accountType").append("=").append((URLEncoder.encode("GOOGLE", "UTF-8")));
			buf.append("&Email").append("=").append((URLEncoder.encode(SENDER_ID, "UTF-8")));
			buf.append("&Passwd").append("=").append((URLEncoder.encode(SENDER_PW, "UTF-8")));
			buf.append("&service").append("=").append((URLEncoder.encode("ac2dm", "UTF-8")));
			buf.append("&source").append("=").append((URLEncoder.encode("myco-pushapp-1.0", "UTF-8")));

			request.setRequestMethod("POST");
			request.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			request.setRequestProperty("Content-Length", buf.toString().getBytes().length+"");

			post = new OutputStreamWriter(request.getOutputStream());
			post.write(buf.toString());
			post.flush();

			int code = request.getResponseCode();
			AppLogger.write(Level.INFO, "response code: " + request.getResponseCode());
			AppLogger.write(Level.INFO, "response message: " + request.getResponseMessage());
			if (code == 200) {
				BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream()));
				buf = new StringBuilder();
				String inputLine;
				while ((inputLine = in.readLine()) != null) {
					if (inputLine.startsWith("Auth=")) {
						authToken = inputLine.substring(5);
					}
					buf.append(inputLine);
				}
				post.close();
				in.close();
				AppLogger.write(Level.INFO, "response from C2DM server: " + buf.toString());

			} 
			else if (code == 403) 
			{
				//TODO: handle error conditions
				AppLogger.write(Level.WARNING, "recieved from C2DM Server : code 403");
			}

			if (authToken != null) 
			{
				AppLogger.write(Level.INFO, "storing auth token: " + authToken);
				_db.saveAuthToken(authToken);
			}

			return authToken;

		} 
		catch (Exception e) 
		{
			AppLogger.write(Level.WARNING, "unable to make https post request to c2dm server");
			try 
			{
				Thread.sleep(1000);
			} 
			catch (InterruptedException e1) 
			{
				e1.printStackTrace();
			}

			return retrieveNewAuthToken();
		}
	}


	//returns the authToken String, if needForRequest set to be true - requests a new authToken
	private String getAuthToken(boolean needForRequest)
	{
		String authToken;
		if(needForRequest)
		{
			return retrieveNewAuthToken();
		}
		else
		{
			authToken = _db.getAuthToken();
			if (authToken != null) 
			{
				AppLogger.write(Level.INFO, "retrieved auth token from db: " + authToken);
				return authToken;
			}
			else
			{
				return retrieveNewAuthToken();
			}
		}
	}

	private void sendMessage(String authToken, String collapseKey, String registrationId, String message)
	throws Exception
	{
		AppLogger.write(Level.INFO, "sending message...");

		HttpsURLConnection.setDefaultHostnameVerifier(new FakeHostnameVerifier()); 
		URL url = new URL("https://android.apis.google.com/c2dm/send");
		HttpsURLConnection request = (HttpsURLConnection) url.openConnection();
		request.setDoOutput(true);
		request.setDoInput(true);

		StringBuilder buf = new StringBuilder();
		buf.append("registration_id").append("=").append((URLEncoder.encode(registrationId, "UTF-8")));
		buf.append("&collapse_key").append("=").append((URLEncoder.encode(collapseKey, "UTF-8")));
		buf.append("&data.message").append("=").append((URLEncoder.encode(message, "UTF-8")));

		request.setRequestMethod("POST");
		request.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
		request.setRequestProperty("Content-Length", buf.toString().getBytes().length+"");
		request.setRequestProperty("Authorization", "GoogleLogin auth=" + authToken);

		OutputStreamWriter post = new OutputStreamWriter(request.getOutputStream());
		post.write(buf.toString());
		post.flush();

		int code = request.getResponseCode();
		AppLogger.write(Level.INFO, "response code: " + request.getResponseCode());
		AppLogger.write(Level.INFO, "response message: " + request.getResponseMessage());
		if (code == 200) 
		{
			//TODO: check for an error and if so, handle
			//I didn't check for an error yet!
			BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream()));
			buf = new StringBuilder();
			String inputLine;
			while ((inputLine = in.readLine()) != null) {
				buf.append(inputLine);
			}
			post.close();
			in.close();

			AppLogger.write(Level.INFO, "response from C2DM server:\n" + buf.toString());			
		} 
		else if (code == 503) 
		{
			//TODO: check for Retry-After header; use exponential backoff and try again
			AppLogger.write(Level.WARNING, "recieved from C2DM Server : code 503");
		} 
		else if (code == 401) 
		{
			//TODO: get a new auth token
			AppLogger.write(Level.WARNING, "recieved from C2DM Server : code 401");
			sendMessage(getAuthToken(true), collapseKey, registrationId, message);
		}
		else
		{
			AppLogger.write(Level.WARNING, "un-supported return code was returned!!!!! At 'Register'");
		}
	}


}
